package metadatabrowser;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.StatusLineManager;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.window.ApplicationWindow;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.TableColumn;

import swing2swt.layout.BorderLayout;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.layout.RowData;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.custom.StackLayout;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.grouplayout.GroupLayout;
import swing2swt.layout.BoxLayout;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;

import at.fhj.itm.metadata.Constraint;
import at.fhj.itm.metadata.ForeignKeyConstraint;
import at.fhj.itm.metadata.Index;
import at.fhj.itm.metadata.MetadataProvider;
import at.fhj.itm.metadata.Procedure;
import at.fhj.itm.metadata.Schema;
import at.fhj.itm.metadata.Sequence;
import at.fhj.itm.metadata.impl.postgres.PostgresSchema;
import at.fhj.itm.refactor.RefactorException;
import at.fhj.itm.refactor.RefactoringSuite;
import at.fhj.itm.refactor.RefactoringSuiteAssembler;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.jface.text.TextViewer;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.Separator;

public class RefactorFrontEndMainWindow extends ApplicationWindow {
	private static final String DEFAULT_JDBC_STRING = "jdbc:postgresql://localhost/dellstore2";
	private static final String STATUS_NO_CONNECTION = "No connection to database";
	private Text textJdbcConnection;
	private Text txtSqlCode;

	
	private SplitTableDialog splitTableDialog;
	
	private List<Action> refactoringActions;
	private RefactoringSuite suite;
	private Button btnConnect;
	private Tree treeSchema;
	private TableViewer propertiesTableViewer;
	private Table tblProperties;
	private PropertiesColumnBuilder propertiesColumnBuilder;
	private TreeViewer treeSchemaViewer;
	private Action actionQuit;
	private Action actionSplitTable;
	/**
	 * Create the application window.
	 */
	public RefactorFrontEndMainWindow() {
		super(null);
		createActions();
		addStatusLine();
		addMenuBar();
		propertiesColumnBuilder = new PropertiesColumnBuilder();
	}

	/**
	 * Create contents of the application window.
	 * 
	 * @param parent
	 */
	@Override
	protected Control createContents(Composite parent) {
		Composite container = new Composite(parent, SWT.NONE);
		container.setLayout(new BorderLayout(0, 0));

		Composite composite = new Composite(container, SWT.NONE);
		composite.setLayoutData(BorderLayout.NORTH);
		composite.setLayout(new GridLayout(3, false));

		Label lblJdbcUrl = new Label(composite, SWT.NONE);
		lblJdbcUrl.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false,
				false, 1, 1));
		lblJdbcUrl.setText("JDBC URL:");

		textJdbcConnection = new Text(composite, SWT.BORDER);
		textJdbcConnection.setLayoutData(new GridData(SWT.FILL, SWT.CENTER,
				true, false, 1, 1));

		btnConnect = new Button(composite, SWT.NONE);
		btnConnect.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				connectDatabase();
			}
		});
		btnConnect.setText("Connect");

		SashForm sashForm = new SashForm(container, SWT.VERTICAL);
		sashForm.setLayoutData(BorderLayout.CENTER);

		SashForm sashForm_1 = new SashForm(sashForm, SWT.NONE);

		treeSchemaViewer = new TreeViewer(sashForm_1, SWT.BORDER);
		treeSchemaViewer.addSelectionChangedListener(new ISelectionChangedListener() {
			public void selectionChanged(SelectionChangedEvent ev) {
				onMetadataItemSelected(ev);
			}
		});
		treeSchema = treeSchemaViewer.getTree();

		propertiesTableViewer = new TableViewer(sashForm_1, SWT.BORDER
				| SWT.FULL_SELECTION);
		tblProperties = propertiesTableViewer.getTable();
		sashForm_1.setWeights(new int[] { 1, 1 });

		txtSqlCode = new Text(sashForm, SWT.BORDER | SWT.H_SCROLL
				| SWT.V_SCROLL | SWT.CANCEL | SWT.MULTI);
		sashForm.setWeights(new int[] { 295, 187 });

		textJdbcConnection.setText(DEFAULT_JDBC_STRING);
		
		return container;
	}
	private void initSchemaTreeViewer(){
		  List<? extends Schema> metadata = suite.getMetadataProvider().getSchema();
		
          ITreeContentProvider contentProvider = new SchemaTreeContentProvider(metadata);
          ILabelProvider labelProvider = new SchemaTreeLabelProvider();
          treeSchemaViewer.setContentProvider(contentProvider);
          treeSchemaViewer.setLabelProvider(labelProvider);
          treeSchemaViewer.setInput(labelProvider);
          
	}
    private void initTableViewer()
    {
        for (TableColumn c : tblProperties.getColumns())
        {
            c.dispose();

        }
        propertiesTableViewer = new TableViewer(tblProperties);


        propertiesColumnBuilder.buildColumns(propertiesTableViewer);
        propertiesTableViewer.setLabelProvider(new TableTableLabelProvider());

    }

	private void onMetadataItemSelected(SelectionChangedEvent ev) {
		if (ev.getSelection().isEmpty()) {
			return;
		}

		if (ev.getSelection() instanceof IStructuredSelection) {

			IStructuredSelection sel = (IStructuredSelection) ev.getSelection();
			Object o = sel.toList().get(0);
			if (o instanceof Schema) {
				initTableViewer();
				String code = suite.getCodeGenerator().generateSchema(
						(Schema) o);
				txtSqlCode.setText(code);
			}
			if (o instanceof at.fhj.itm.metadata.Table) {
				initTableViewer();
				propertiesTableViewer
						.setContentProvider(new TableContentProvider());
				propertiesTableViewer.setInput(o);
				String code = suite.getCodeGenerator().generateTable(
						(at.fhj.itm.metadata.Table) o);
				txtSqlCode.setText(code);

			} else if (o instanceof at.fhj.itm.metadata.Column) {
				initTableViewer();
				propertiesTableViewer
						.setContentProvider(new ColumnContentProvider());
				propertiesTableViewer.setInput(o);
				String code = suite.getCodeGenerator().generateColumn(
						(at.fhj.itm.metadata.Column) o);
				txtSqlCode.setText(code);
			} else if (o instanceof ForeignKeyConstraint) {
				initTableViewer();
				propertiesTableViewer
						.setContentProvider(new ForeignKeyConstraintContentProvider());
				propertiesTableViewer.setInput(o);
				String code = suite.getCodeGenerator().generateConstraint(
						(at.fhj.itm.metadata.Constraint) o);
				txtSqlCode.setText(code);
			} else if (o instanceof Constraint) {
				initTableViewer();
				propertiesTableViewer
						.setContentProvider(new SimpleConstraintContentProvider());
				propertiesTableViewer.setInput(o);
				String code = suite.getCodeGenerator().generateConstraint(
						(at.fhj.itm.metadata.Constraint) o);
				txtSqlCode.setText(code);
			} else if (o instanceof Procedure) {
				initTableViewer();
				;
				propertiesTableViewer
						.setContentProvider(new ProcedureContentProvider());
				String code = suite.getCodeGenerator().generateProcedure(
						(Procedure) o);
				propertiesTableViewer.setInput(o);
				txtSqlCode.setText(code);
			} else if (o instanceof Sequence) {
				initTableViewer();
				propertiesTableViewer
						.setContentProvider(new SequenceContentProvider());
				propertiesTableViewer.setInput(o);
				String code = suite.getCodeGenerator().generateSequence(
						(Sequence) o);
				txtSqlCode.setText(code);

			} else if (o instanceof Index) {
				initTableViewer();
				propertiesTableViewer
						.setContentProvider(new IndexContentProvider());
				propertiesTableViewer.setInput(o);
				String code = suite.getCodeGenerator().generateIndex((Index) o);
				txtSqlCode.setText(code);
			}
		}

	}

	private void connectDatabase() {
		try {
			suite = RefactoringSuiteAssembler.getInstance()
					.createRefactoringSuite(textJdbcConnection.getText(),
							"postgres", "postgres");
			btnConnect.setEnabled(false);
			initSchemaTreeViewer();
			activateRefactoringActions(true);
			getStatusLineManager().setMessage("Connected to database.");
		} catch (RefactorException e) {
			StringWriter sw = new StringWriter();
			e.printStackTrace(new PrintWriter(sw));

			IStatus status = new Status(IStatus.ERROR, "MetadataBrowser",
					sw.toString());
			ErrorDialog.openError(getShell(), "Error opening database",
					"Error while connecting to database", status);
			getStatusLineManager().setErrorMessage(
					"Error connecting to database.");
		}
	}

	private void showSplitTableDialog(){
		if(splitTableDialog == null){
			splitTableDialog = new SplitTableDialog(this.getShell(), suite);
		}
		
		splitTableDialog.open();
	}
	private void activateRefactoringActions(boolean enable){
		for(Action a : refactoringActions){
			a.setEnabled(enable);
		}
	}
	
	/**
	 * Create the actions.
	 */
	private void createActions() {
		refactoringActions = new ArrayList<Action>();
		
		
		{
			actionQuit = new Action("Quit") {
				@Override
				public void run() {
					RefactorFrontEndMainWindow.this.getShell().close();
				}
			};
		}
		{
		actionSplitTable = new Action("Split Table") {
			public void run() {
				showSplitTableDialog();
			};
				
			};
		actionSplitTable.setEnabled(false);
		}
		
		refactoringActions.add(actionSplitTable);
	}
	
	

	/**
	 * Create the menu manager.
	 * 
	 * @return the menu manager
	 */
	@Override
	protected MenuManager createMenuManager() {
		MenuManager menuManager = new MenuManager("menu");
		
		MenuManager mmFile = new MenuManager("File");
		menuManager.add(mmFile);
		mmFile.add(actionQuit);
		
		MenuManager mmRefactorings = new MenuManager("Refactorings");
		menuManager.add(mmRefactorings);
		mmRefactorings.add(actionSplitTable);
		return menuManager;
	}

	/**
	 * Create the toolbar manager.
	 * 
	 * @return the toolbar manager
	 */
	@Override
	protected ToolBarManager createToolBarManager(int style) {
		ToolBarManager toolBarManager = new ToolBarManager(style);
		return toolBarManager;
	}

	/**
	 * Create the status line manager.
	 * 
	 * @return the status line manager
	 */
	@Override
	protected StatusLineManager createStatusLineManager() {
		StatusLineManager statusLineMgr = new StatusLineManager();
		return statusLineMgr;
	}

	/**
	 * Launch the application.
	 * 
	 * @param args
	 */
	public static void main(String args[]) {
		try {
			RefactorFrontEndMainWindow window = new RefactorFrontEndMainWindow();
			window.setBlockOnOpen(true);
			window.open();
			Display.getCurrent().dispose();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Configure the shell.
	 * 
	 * @param newShell
	 */
	@Override
	protected void configureShell(Shell newShell) {
		super.configureShell(newShell);
		newShell.setText("Refactor Front-End Metadata Browser");
		getStatusLineManager().setMessage(STATUS_NO_CONNECTION);
	}

	/**
	 * Return the initial size of the window.
	 */
	@Override
	protected Point getInitialSize() {
		return new Point(742, 572);
	}

}
